package com.cv.framework.security.core.utils;

import cn.hutool.core.util.ObjUtil;
import com.cv.framework.common.enums.UserTypeEnum;
import com.cv.framework.security.config.SecurityProperties;
import com.cv.framework.security.core.constants.SecurityConstants;
import com.cv.system.api.token.form.LoginUser;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import javax.crypto.SecretKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

/**
 * Jwt Token 工具类
 *
 * @author Charles_XDXD
 */
@Slf4j
public class JwtUtil {

    private static SecurityProperties properties;

    private static SecretKey key;

    public static void init(SecurityProperties securityProperties) {
        properties = securityProperties;
        key = Keys.hmacShaKeyFor(securityProperties.getJwtSecretKey().getBytes());
        log.info("[JwtUtils]: 初始化成功");
    }

    /**
     * 生成并返回 JWT Token
     *
     * @param loginUser 用户登录信息
     * @return 令牌信息
     */
    public static String createToken(LoginUser loginUser) {
        Date now = new Date();
        Date expiration = new Date(now.getTime() + properties.getJwtExpiration());

        Map<String, Object> claimsMap = new HashMap<>();
        claimsMap.put(SecurityConstants.USER_ID, loginUser.getId());
        claimsMap.put(SecurityConstants.USER_TYPE, loginUser.getUserType());
        claimsMap.put(SecurityConstants.ROLE_ID, loginUser.getRoleId());
        claimsMap.put(SecurityConstants.ROLE_NAME, loginUser.getRoleName());

        return Jwts.builder()
                .setClaims(claimsMap)
                .setIssuedAt(now)
                .setExpiration(expiration)
                .signWith(key)
                .compact();
    }

    /**
     * 解析 JWT Token
     *
     * @param token JWT Token
     * @return 用户登录信息
     */
    public static LoginUser parseToken(String token) {
        if (!StringUtils.hasText(token)) {
            return null;
        }

        try {
            Claims claims = Jwts.parserBuilder()
                    .setSigningKey(key)
                    .build()
                    .parseClaimsJws(token)
                    .getBody();

            LoginUser loginUser = new LoginUser();
            loginUser.setId(claims.get(SecurityConstants.USER_ID, Long.class));
            loginUser.setUserType(claims.get(SecurityConstants.USER_TYPE, Integer.class));
            loginUser.setRoleId(claims.get(SecurityConstants.ROLE_ID, Long.class));
            loginUser.setRoleName(claims.get(SecurityConstants.ROLE_NAME, String.class));
            return loginUser;
        } catch (JwtException e) {
            log.error("[JwtUtils]: Jwt Token 解析失败", e);
            return null;
        }
    }

    /**
     * 校验 Token
     *
     * @param token JWT Token
     * @return 校验结果
     */
    public static boolean validateToken(String token) {
        if (StringUtils.hasText(token)) {
            try {
                Claims claims = Jwts.parserBuilder()
                        .setSigningKey(key)
                        .build()
                        .parseClaimsJws(token)
                        .getBody();

                // 检查 token 是否过期
                return !claims.getExpiration().before(new Date());
            } catch (JwtException e) {
                log.error("[JwtUtils]: Jwt Token 校验失败", e);
            }
        }
        return false;
    }

    /**
     * 刷新 JWT Token
     *
     * @param token 原 token
     * @return 新 token，如果原 token 无效则返回 null
     */
    public static String refreshToken(String token) {
        if (!StringUtils.hasText(token) || !validateToken(token)) {
            return null;
        }

        LoginUser loginUser = parseToken(token);

        if (ObjUtil.isEmpty(loginUser)) {
            return null;
        }

        return createToken(loginUser);
    }

    /**
     * 判断 Token 有效期是否小于等于刷新时间
     *
     * @param token JWT Token
     * @return 如果有效期小于等于刷新时间，返回 true；否则返回 false
     */
    public static boolean isTokenExpiringSoon(String token) {
        if (StringUtils.hasText(token)) {
            try {
                Claims claims = Jwts.parserBuilder()
                        .setSigningKey(key)
                        .build()
                        .parseClaimsJws(token)
                        .getBody();

                // 获取 token 的过期时间
                long expirationTimeInMillis = claims.getExpiration().getTime();
                long currentTimeInMillis = System.currentTimeMillis();

                // 检查有效期是否小于等于刷新范围
                return (expirationTimeInMillis - currentTimeInMillis) <= properties.getJwtRefreshExpiration();
            } catch (JwtException e) {
                log.error("[JwtUtils]: Jwt Token 解析失败", e);
            }
        }
        return false;
    }

    /**
     * 获取当前用户 id
     *
     * @param token JWT Token
     * @return 用户 id
     */
    public static Long getUserId(String token) {
        return Optional.ofNullable(parseToken(token))
                .orElse(new LoginUser().setId(null))
                .getId();
    }

    /**
     * 获取当前用户类型
     *
     * @param token JWT Token
     * @return 用户类型 关联 {@link UserTypeEnum}
     */
    public static Integer getUserType(String token) {
        return Optional.ofNullable(parseToken(token))
                .orElse(new LoginUser().setUserType(null))
                .getUserType();
    }

    /**
     * 获取当前用户角色 id
     *
     * @param token JWT Token
     * @return 角色 id
     */
    public static Long getRoleId(String token) {
        return Optional.ofNullable(parseToken(token))
                .orElse(new LoginUser().setRoleId(null))
                .getRoleId();
    }

    /**
     * 获取当前用户角色名称
     *
     * @param token JWT Token
     * @return 角色名称
     */
    public static String getRoleName(String token) {
        return Optional.ofNullable(parseToken(token))
                .orElse(new LoginUser().setRoleName(null))
                .getRoleName();
    }

}